﻿using System;
using System.Runtime.InteropServices;
using LDSCore;
using LDSModel.Item;

namespace LDSBusiness
{
    /// <summary>
    /// 消息发送者
    /// </summary>
    public sealed class DataSender : DataOperator
    {
        static readonly object ObjLock = new object();

        private DataSender()
        {
        }

        #region 消息发送

        #region 上位机命令

        /// <summary>
        /// 查询设备状态
        /// </summary>
        /// <param name="destAddress"></param>
        public void CheckState(int destAddress)
        {
            byte[] msgData = ConstractData(Constant.MsgSrcAddr, destAddress, new byte[] { }, MsgType.ZGCMD_CHECK_STATE, MsgType.ZGCMD_ERR_UNKNOWN);

            SendData(msgData);
        }

        /// <summary>
        /// 设备复位
        /// </summary>
        /// <param name="destAddress"></param>
        public void Reset(int destAddress)
        {
            byte[] msgData = ConstractData(Constant.MsgSrcAddr, destAddress, new byte[] { }, MsgType.ZGCMD_RESET, MsgType.ZGCMD_ERR_UNKNOWN);

            SendData(msgData);
        }

        /// <summary>
        /// 恢复默认配置
        /// </summary>
        /// <param name="destAddress"></param>
        public void Restore(int destAddress)
        {
            byte[] msgData = ConstractData(Constant.MsgSrcAddr, destAddress, new byte[] { }, MsgType.ZGCMD_DEFAULT, MsgType.ZGCMD_ERR_UNKNOWN);

            SendData(msgData);
        }

        /// <summary>
        /// 读取系统配置信息
        /// </summary>
        /// <param name="destAddress"></param>
        public void ReadSysInfo(int destAddress)
        {
            byte[] msgData = ConstractData(Constant.MsgSrcAddr, destAddress, new byte[] { }, MsgType.ZGCMD_READ_SYSINFO, MsgType.ZGCMD_ERR_UNKNOWN);

            SendData(msgData);
        }

        /// <summary>
        /// 读取时间
        /// </summary>
        /// <param name="destAddress"></param>
        public void ReadSysTime(int destAddress)
        {
            byte[] msgData = ConstractData(Constant.MsgSrcAddr, destAddress, new byte[] { }, MsgType.ZGCMD_READ_SYSTIME, MsgType.ZGCMD_ERR_UNKNOWN);

            SendData(msgData);
        }

        /// <summary>
        /// 设置系统时间
        /// </summary>
        /// <param name="destAddress"></param>
        public void SetSysTime(int destAddress)
        {
            var dt = new RtcDateTime(DateTime.Now);
            var msgData = ConstractData(Constant.MsgSrcAddr, destAddress, dt.ToBytes(), MsgType.ZGCMD_SET_SYSTIME, MsgType.ZGCMD_ERR_UNKNOWN);

            SendData(msgData);
        }

        /// <summary>
        /// 读取一条记录
        /// </summary>
        /// <param name="destAddress"></param>
        /// <param name="index"></param>
        public void ReadRecord(int destAddress, long index)
        {
            var data = new byte[2];
            data[0] = Convert.ToByte(index & 0xff);
            data[1] = Convert.ToByte((index / 256) & 0xff);
            var msgData = ConstractData(Constant.MsgSrcAddr, destAddress, data, MsgType.ZGCMD_READ_RECORD, MsgType.ZGCMD_ERR_UNKNOWN);

            SendData(msgData);
        }

        /// <summary>
        /// 应答下位机
        /// </summary>
        /// <param name="msg">源地址</param>
        /// <param name="isOk">是否成功</param>
        public void Answer(MsgItem msg, bool isOk)
        {
            var type = MsgType.ZGCMD_ANSWER_OK;
            if (!isOk) type = MsgType.ZGCMD_ANSWER_ERROR;

            var msgData = ConstractData(msg.DestAddr, msg.SrcAddr, null, type, (MsgType)msg.MsgType);

            SendData(msgData);
        }

        #endregion

        #region 下位机命令

        public void SendData(string data)
        {
            if ((data.Length % 2) != 0)
                data += "";
            var msgData = new byte[data.Length / 2];
            for (var i = 0; i < msgData.Length; i++)
                msgData[i] = Convert.ToByte(data.Substring(i * 2, 2), 16);

            SendData(msgData);
        }

        public void SendRecord(int srcAddress, RecordItem record)
        {
            SendRecord(srcAddress, record, MsgType.ZGCMD_ERR_UNKNOWN);
        }

        /// <summary>
        /// 发送一条记录
        /// </summary>
        /// <param name="srcAddress"></param>
        /// <param name="record"></param>
        /// <param name="exceptionType"></param>
        public void SendRecord(int srcAddress, RecordItem record, MsgType exceptionType)
        {
            byte[] msgData = ConstractData(srcAddress, Constant.MsgSrcAddr, record.ToBytes(), MsgType.ZGCMD_SEND_RECORD, exceptionType);

            SendData(msgData);
        }

        /// <summary>
        /// 发送心跳包
        /// </summary>
        public void SendHeartjump(int srcAddress)
        {
            byte[] msgData = ConstractData(srcAddress, Constant.MsgSrcAddr, null, MsgType.ZGCMD_NET_HEARTBEAT, MsgType.ZGCMD_ERR_UNKNOWN);

            SendData(msgData);
        }

        /// <summary>
        /// 发送系统信息
        /// </summary>
        /// <param name="destAddress"></param>
        public void SendSystemInfo(int destAddress)
        {
            var info = new SysInfoItem {ID = "89675789", Name = "dgdfgd", ProductionDate = DateTime.Now};
            var msgData = ConstractData(destAddress, Constant.MsgSrcAddr, info.ToBytes(), MsgType.ZGCMD_SEND_SYSINFO, MsgType.ZGCMD_ERR_UNKNOWN);

            SendData(msgData);
        }

        /// <summary>
        /// 发送系统时间
        /// </summary>
        /// <param name="destAddress"></param>
        public void SendSystemTime(int destAddress)
        {
            var time = new RtcDateTime(DateTime.Now);
            var msgData = ConstractData(destAddress, Constant.MsgSrcAddr, time.ToBytes(), MsgType.ZGCMD_SEND_SYSTIME, MsgType.ZGCMD_ERR_UNKNOWN);

            SendData(msgData);
        }

        #endregion

        #endregion

        #region 私有方法

        /// <summary>
        /// 构造消息数组
        /// </summary>
        /// <param name="msgSrcAddress"></param>
        /// <param name="msgDestAddress"></param>
        /// <param name="data"></param>
        /// <param name="msgType"></param>
        /// <param name="exceptType"></param>
        /// <returns></returns>
        byte[] ConstractData(int msgSrcAddress, int msgDestAddress, byte[] data, MsgType msgType, MsgType exceptType)
        {
            var head = new MsgHead
                {
                    SrcAddr = (ushort) msgSrcAddress,
                    DestAddr = (ushort) msgDestAddress,
                    MsgID = (ushort) (Properties.Settings.Default.MsgId + 1)
                };

            //lock (objLock)
            //{
            Properties.Settings.Default.MsgId = head.MsgID;
            Properties.Settings.Default.Save();
            //}

            head.MsgType = (ushort)msgType;
            head.Exception = (ushort)exceptType;

            int byteLength = Marshal.SizeOf(typeof(MsgHead)) + 2;

            if (null != data && data.Length > 0)
            {
                head.DataLength = (ushort)data.Length;
                byteLength += data.Length;
            }
            else
            {
                head.DataLength = 0;
            }

            var bytes = new byte[byteLength];

            var headBytes = head.ToBytes();
            Array.Copy(headBytes, bytes, headBytes.Length);
            if (null != data && data.Length > 0)
                Array.Copy(data, 0, bytes, headBytes.Length, data.Length);

            var chkSum = GetCheckSum(bytes, bytes.Length - 2);
            bytes[bytes.Length - 2] = Convert.ToByte((chkSum / 256) & 0xFF);
            bytes[bytes.Length - 1] = Convert.ToByte(chkSum & 0xFF);

            return bytes;
        }
        
        private static void SendData(byte[] msgData)
        {
            lock (ObjLock)
            {
                if (SerialPort.IsOpen)
                    SerialPort.Write(msgData, 0, msgData.Length);
            }
        }

        /// <summary>
        /// 获得校验码
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="size"></param>
        /// <returns></returns>
        public UInt16 GetCheckSum(byte[] bytes, int size)
        {
            UInt16 chkSum = 0;
            for (int i = 0; i < size; i++)
            {
                chkSum += bytes[i];
            }
            return chkSum;
        }

        #endregion

        static readonly DataSender _instance = new DataSender();
        /// <summary>
        /// 单例
        /// </summary>
        public static DataSender Instance
        {
            get
            {
                return _instance;
            }
        }
    }
}
